⚡ 30 秒速记

  • 数据赋值与页面更新不是同一时刻
  • 同轮更新入队并去重,减少重复渲染
  • 调度顺序考虑父子与新增任务
  • 修改后 await nextTick() 等待本轮刷新
  • 页面已更新不代表浏览器一定已绘制

异步更新是先把数据变化记下来,再集中刷新页面,避免每次赋值都重复渲染。 同一轮里多次修改同一组件相关数据,调度器可以去重并按合适顺序处理。需要读取更新后的节点时,在修改之后等待 nextTick。它等待框架的刷新边界,不是通用定时器,也不保证图片、网络或浏览器绘制已经完成,等待对象要与业务需求一致。

版本校准: 本文出现 Object.defineProperty、Dep、Watcher 和双端 Diff 时,主要描述 Vue 2 实现;Vue 3 使用 Proxy、effect 与新的渲染器路径。Vue 2 已于 2023-12-31 结束维护,新项目应以 Vue 3 为基线,旧项目参考 Vue 2 EOL 官方说明 制定迁移与安全策略。

本文主要分析 Vue 从 Data 更新,到通知 Watcher 异步更新视图的流程,也就是下图中的橙色部分。

我们先来回顾一下图中的几个对象:

  • Data 对象:Vue 中的 data 方法中返回的对象。
  • Dep 对象:每一个 Data 属性都会创建一个 Dep,用来搜集所有使用到这个 Data 的 Watcher 对象。
  • Watcher 对象:主要用于渲染 DOM。

接下来,我们就开始分析这个流程。

# Vue 异步更新 DOM 原理

很多同学都知道,Vue 中的数据更新是异步的,意味着我们在修改完 Data 之后,并不能立刻获取修改后的 DOM 元素。

<template>
  <div>
    <span id="text">{{ message }}</span>
    <button @click="changeData">
      changeData
    </button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: "hello",
    };
  },
  methods: {
    changeData() {
      this.message = "hello world";
      const textContent = document.getElementById("text").textContent;
      // 直接获取,不是最新的
      console.log(textContent === "hello world"); // false
            // $nextTick 回调中,是最新的
      this.$nextTick(() => {
        const textContent = document.getElementById("text").textContent;
        console.warn(textContent === "hello world"); // true
      });
    },
  },
};
</script>
webapp
公众号
开发者导航
切换夜间模式
点击侧边栏上一篇
点击侧边栏下一篇
折叠侧边栏
收起全部